﻿using Fabrication.Tests.Mocks;
using Fabrication.Tests.TestClasses;
using NUnit.Framework;

namespace Fabrication.Tests
{
    [TestFixture]
    public class scope_rules
    {
        [Test]
        public void should_allow_adding_new_default_type_provider()
        {
            var scope = new FabricationScope();
            scope.SetConvention<uint, MockConstantValueProvider<uint>>();

            var builtItem = scope.Generate<SinglePropertyClass<uint>>();

            Assert.That(builtItem.Value, Is.EqualTo((uint)0));
        }

        [Test]
        public void should_allow_overriding_default_type_provider()
        {
            var scope = new FabricationScope();
            scope.SetConvention<int, MockConstantValueProvider<int>>();

            var builtItem = scope.Generate<SinglePropertyClass<int>>();

            Assert.That(builtItem.Value, Is.EqualTo(0));
        }

        [Test]
        public void should_allow_removing_typed_convention()
        {
            var scope = new FabricationScope();
            scope.RemoveConvention<string>("FullName");

            var builtItem = scope.Generate<NamesConventionClass>();

            Assert.That(builtItem.FullName, Is.EqualTo("FullName1"));
        }

        [Test]
        public void should_allow_removing_all_conventions_with_a_specific_name()
        {
            var scope = new FabricationScope();
            scope.SetConvention<int, MockConstantValueProvider<int>>("FirstName");
            scope.RemoveConvention("FirstName");

            var builtItem = scope.Generate<NumericNameClass>();

            Assert.That(builtItem.FirstName, Is.EqualTo(1));
        }

        [Test]
        public void should_allow_removing_all_named_conventions()
        {
            var scope = new FabricationScope();
            scope.RemoveAllNamedConventions();

            var builtItem = scope.Generate<NamesConventionClass>();

            Assert.That(builtItem.FirstName, Is.EqualTo("FirstName1"));
            Assert.That(builtItem.GivenName, Is.EqualTo("GivenName1"));
            Assert.That(builtItem.LastName, Is.EqualTo("LastName1"));
            Assert.That(builtItem.Surname, Is.EqualTo("Surname1"));
            Assert.That(builtItem.FullName, Is.EqualTo("FullName1"));
        }

        [Test]
        public void should_allow_setting_convention_with_duplicate_name_of_different_type()
        {
            var scope = new FabricationScope();
            scope.SetConvention<int, MockConstantValueProvider<int>>("FirstName");

            var builtItem = scope.Generate<NumericNameClass>();

            Assert.That(builtItem.FirstName, Is.EqualTo(0));
        }

        [Test]
        public void should_not_override_convention_with_duplicate_name_of_different_type()
        {
            var scope = new FabricationScope();
            scope.SetConvention<int, MockConstantValueProvider<int>>("FirstName");

            var builtItem = scope.Generate<NamesConventionClass>();

            Assert.That(builtItem.FirstName, Is.Not.Null.And.Not.ContainsSubstring("FirstName"));
        }

        [Test]
        public void should_allow_adding_convention_by_provider_type()
        {
            var scope = new FabricationScope();
            scope.SetConvention<string, MockStringValueProvider>("Vorname");

            var builtItem = scope.Generate<GermanClass>();

            Assert.That(builtItem.Vorname, Is.EqualTo("VornameMock"));
        }

        [Test]
        public void should_allow_adding_convention_by_provider_instance()
        {
            var scope = new FabricationScope();
            scope.SetConvention<string>(new MockStringValueProvider(), "Vorname");

            var builtItem = scope.Generate<GermanClass>();

            Assert.That(builtItem.Vorname, Is.EqualTo("VornameMock"));
        }

        [Test]
        public void should_allow_overriding_convention_by_provider_type()
        {
            var scope = new FabricationScope();
            scope.SetConvention<string, MockStringValueProvider>("FirstName");

            var builtItem = scope.Generate<NamesConventionClass>();

            Assert.That(builtItem.FirstName, Is.EqualTo("FirstNameMock"));
        }

        [Test]
        public void should_allow_overriding_convention_by_provider_instance()
        {
            var scope = new FabricationScope();
            scope.SetConvention<string>(new MockStringValueProvider(), "FirstName");

            var builtItem = scope.Generate<NamesConventionClass>();

            Assert.That(builtItem.FirstName, Is.EqualTo("FirstNameMock"));
        }
    }

    class GermanClass
    {
        public string Vorname { get; set; }
    }

    class NumericNameClass
    {
        public int FirstName { get; set; }
    }
}
